/*! \file kru_mem.c
    Defines K&R Unit memory management.
	All the functions implemented here here abort on out
	of memory condition.
    These functions are much like the (in)famous 'xmalloc'
    package. Some complain that simply aborting in low
    memory situation is bad. I disagree, on modern OSes with
    virtual memory an out of memory means that your code is
    wrong. It is better to catch these errors during tests
    than in production code co enter 'kru_malloc' and
    companions.
    This memory manager in general doesn't detect heap
    corruption or memory leaks. To hunt them use 'jmalloc',
    'dmalloc', 'MallocDebug' or other memory debugging tool.
    It is important though to use this manager while testing your
    code either by directly using 'kru_xxx' routines or by
    redefining the RTL ones, see 'kru.h' for more.
    To abort tests execution I use 'exit' call. If your tests
    should perform some global clean up even in case of out
    of memory situation, put the clean up code to an 'atexit'
    handler.
*/

#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>

#include "kru.h"

#undef malloc
#undef calloc
#undef realloc
#undef kru_free

/*! \def LR_POOL_SIZE
	\brief Last resort memory pool.
 */

#define LR_POOL_SIZE 4096

// A static here is OK, malloc's heap is global
// However, in theory this has to be protected by a mutex
/*@shared@*/ /*@null@*/ static void* lastResortPool = NULL;

static void InitLRPool(void)
{
	if(NULL == lastResortPool)
	{
		lastResortPool = malloc(LR_POOL_SIZE);
		if(NULL == lastResortPool)
		{
			// Are we running on a 16 Kb RAM embedded device???
			fputs("KRU Error: cannot allocate last resort memory pool, aborting tests.", stderr);
			exit(EXIT_FAILURE);
		}
	}
}

void* kru_malloc(size_t size)
{
	void* result;

	InitLRPool();
	result = malloc(size);
	if(NULL == result)
	{
		free(lastResortPool);
		fputs("KRU Error: out of memory (malloc), aborting tests.", stderr);
		exit(EXIT_FAILURE);
	}
	return result;
}

void* kru_calloc(size_t n, size_t size)
{
	void* result;

	InitLRPool();
	result = calloc(n, size);
	if(NULL == result)
	{
		free(lastResortPool);
		fputs("KRU Error: out of memory (calloc), aborting tests.", stderr);
		exit(EXIT_FAILURE);
	}
	return result;
}

void* kru_realloc(void *oldptr, size_t size)
{
	void* result;

	InitLRPool();
	result = realloc(oldptr, size);
	if(NULL == result)
	{
		free(lastResortPool);
		fputs("KRU Error: out of memory (realloc), aborting tests.", stderr);
		exit(EXIT_FAILURE);
	}
	return result;
}
